import { normalizeClass, normalizeStyle } from '@vue/shared'
import { defineComponent, Transition, PropType, inject, ref, StyleValue, CSSProperties, ComponentPublicInstance, unref, computed } from 'vue'
import { MaybeRef } from '@vueuse/core'
import DisplayDirective, { DisplayDirectiveType } from '../_internal/display-directive'

import Align from '../align'
import type { AlignType, AlignResult, TargetPoint } from '../align/interface'

const popupProps = {
  prefixCls: String,
  visible: Boolean,
  getClassNameFromAlign: Function,
  getRootDomNode: Function as PropType<() => HTMLElement>,
  align: Object as PropType<MaybeRef<AlignType>>,
  displayDirective: String as PropType<DisplayDirectiveType>,
  getContainer: Function,
  transition: [Object, Function] as PropType<typeof Transition>,
  transitionName: String,
  mask: Boolean,
  maskTransitionName: String,
  zIndex: Number,
  popupClass: [String, Object, Array],
  popupStyle: [String, Object, Array] as PropType<StyleValue>,
  disabledAlign: [Boolean, Object] as PropType<MaybeRef<Boolean>>,
  disabledAlignInTransiting: [Boolean, Object] as PropType<MaybeRef<Boolean>>,
  point: Object as PropType<TargetPoint>
}

export default defineComponent({
  name: 'TriggerPopup',
  props: popupProps,
  inheritAttrs: false,
  emits: ['align'],
  setup(props, { attrs, slots, emit }) {
    const popupRef = inject('popupRef', ref<HTMLElement>())
    const alignRef = inject('alignRef', ref<ComponentPublicInstance<{ forceAlign: Function }>>())

    // ===================== 禁止对齐 ==========================
    const transiting = ref(false)
    const disabledAlign = computed(() => unref(props.disabledAlign) || (unref(props.disabledAlignInTransiting) && transiting.value) || !props.visible)
    function onEnter() {
      if (!popupRef.value) return
      alignRef.value?.forceAlign()
      ainBegin()
    }
    function ainBegin() {
      transiting.value = true
    }
    function ainEnd() {
      transiting.value = false
    }

    function onAlign(popupEl: Element, align: AlignResult) {
      emit('align', ...arguments)
    }

    function getZIndexStyle() {
      const style: CSSProperties = {}
      if (props.zIndex !== undefined) {
        style.zIndex = props.zIndex
      }
      return style
    }

    function _Transition(_, { slots }) {
      const transition = props.transition || Transition
      return <transition {..._} appear onEnter={onEnter} onAfterEnter={ainEnd} onBeforeLeave={ainBegin} onAfterLeave={ainEnd} v-slots={slots} />
    }

    function renderPopup() {
      const popupInnerProps = {
        ...attrs,
        style: normalizeStyle([attrs.style, props.popupStyle, getZIndexStyle()]),
        class: normalizeClass([attrs.class, props.prefixCls, props.popupClass])
      }
      const name = props.transitionName || props.prefixCls
      const target = props.point || props.getRootDomNode
      return (
        <Align ref={alignRef} target={target} align={props.align} disabled={disabledAlign} onAlign={onAlign}>
          <_Transition name={name}>
            <DisplayDirective visible={props.visible} type={'show'}>
              <div {...popupInnerProps} ref={popupRef}>
                {slots.default?.()}
              </div>
            </DisplayDirective>
          </_Transition>
        </Align>
      )
    }

    function renderMask() {
      if (props.mask) {
        const name = props.maskTransitionName || `${props.prefixCls}-mask`
        return (
          <Transition appear name={name}>
            <div v-show={props.visible} class={`${props.prefixCls}-mask`} style={getZIndexStyle()}></div>
          </Transition>
        )
      }
    }

    return () => (
      <div style='position: absolute; top: 0; left: 0; width: 100%'>
        {renderMask()}
        {renderPopup()}
      </div>
    )
  }
})
